package cn.xylin.zjwh.crack;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.Switch;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author XyLin
 * @date 2020/7/8 21:51:17
 * SettingActivity.java 关于这个界面为什么不使用线程池，原因是感觉没有人会无聊到一直点检查更新，如果真有人这么做了
 * 那也不关我的事😏，毕竟就几个地方需要用子线程，而且这个APP也不一定有很多人用，再者谁会闲着无聊看代码注释......
 **/
public class SettingActivity extends Activity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
    public static final String XML_SETTING = "setting";
    public static final String KEY_CLASS_NAME = "beHookClassName";
    public static final String KEY_MODIFY_TIME = "isModifyTime";
    public static final String KEY_START_TIME = "modifyStartTime";
    public static final String KEY_END_TIME = "modifyEndTime";
    public static final String KEY_MODIFY_STEPS = "isModifySteps";
    public static final String KEY_MAX_STEPS = "maxSteps";
    public static final String KEY_UPDATE_REBOOT = "isUpdateNoReboot";
    public static final String KEY_MODIFY_DIS = "isModifyDis";
    public static final String KEY_MAX_DIS = "maxDis";
    public static final String KEY_POINT_PASS = "isPointPass";
    public static final String KEY_REMOVE_ADS = "isRemoveAds";
    public static final String PKG = "com.zjwh.android_wh_physicalfitness";
    private static final int HAVE_UPDATE = 200;
    private static final String JSON_VERSION_NAME = "versionName";
    private static final String JSON_VERSION_CODE = "versionCode";
    private static final String JSON_DESCRIPTION = "description";
    private static final String JSON_DOWNLOAD_URL = "downloadUrl";
    private static final String JSON_DOM = "dom";
    private static final String JSON_URL = "url";
    private static final Pattern PATTERN_IFRAME_URL = Pattern.compile("/fn\\?[^v2]\\S+\\b");
    private static final Pattern PATTERN_DOWNLOAD_URL = Pattern.compile("\\w{50,100}");
    private SharedPreferences preferences;
    private EditText edtClassName, edtStartTime, edtEndTime, edtMaxSteps, edtDisModify;
    private RelativeLayout rlTimeModify, rlStepsModify, rlDisModify;
    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    private Handler updateHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == HAVE_UPDATE && msg.obj instanceof JSONObject) {
                showUpdateDialog((JSONObject) msg.obj);
            }
        }
    };
    private Runnable antiCodeRunnable;
    private String zjwhVersionName;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.setting_activity);
        preferences = getSharedPreferences(XML_SETTING, Context.MODE_WORLD_READABLE);
        edtClassName = (EditText) findViewById(R.id.edtClassName);
        edtStartTime = (EditText) findViewById(R.id.edtStartTime);
        edtEndTime = (EditText) findViewById(R.id.edtEndTime);
        edtMaxSteps = (EditText) findViewById(R.id.edtMaxSteps);
        edtDisModify = (EditText) findViewById(R.id.edtDisModify);
        rlTimeModify = (RelativeLayout) findViewById(R.id.rlTimeModify);
        rlStepsModify = (RelativeLayout) findViewById(R.id.rlStepsModify);
        rlDisModify = (RelativeLayout) findViewById(R.id.rlDisModify);
        edtClassName.setText(preferences.getString(KEY_CLASS_NAME, ""));
        edtStartTime.setText(dateFormat.format(new Date(preferences.getLong(KEY_START_TIME, System.currentTimeMillis()))));
        edtEndTime.setText(dateFormat.format(new Date(preferences.getLong(KEY_END_TIME, System.currentTimeMillis()))));
        edtMaxSteps.setText(String.valueOf(preferences.getInt(KEY_MAX_STEPS, 0)));
        edtDisModify.setText(String.valueOf(preferences.getInt(KEY_MAX_DIS, 0)));
        Switch shTimeModify = (Switch) findViewById(R.id.shTimeModify);
        Switch shStepsModify = (Switch) findViewById(R.id.shStepsModify);
        Switch shUpdateNoReboot = (Switch) findViewById(R.id.shUpdateNoReboot);
        Switch shDisModify = (Switch) findViewById(R.id.shDisModify);
        Switch shPointPass = (Switch) findViewById(R.id.shPointPass);
        Switch shRemoveAds = (Switch) findViewById(R.id.shRemoveAds);
        shTimeModify.setOnCheckedChangeListener(this);
        shStepsModify.setOnCheckedChangeListener(this);
        shUpdateNoReboot.setOnCheckedChangeListener(this);
        shDisModify.setOnCheckedChangeListener(this);
        shPointPass.setOnCheckedChangeListener(this);
        shRemoveAds.setOnCheckedChangeListener(this);
        shTimeModify.setChecked(preferences.getBoolean(KEY_MODIFY_TIME, false));
        shStepsModify.setChecked(preferences.getBoolean(KEY_MODIFY_STEPS, false));
        shUpdateNoReboot.setChecked(preferences.getBoolean(KEY_UPDATE_REBOOT, false));
        shDisModify.setChecked(preferences.getBoolean(KEY_MODIFY_DIS, false));
        shPointPass.setChecked(preferences.getBoolean(KEY_POINT_PASS, false));
        shRemoveAds.setChecked(preferences.getBoolean(KEY_REMOVE_ADS, true));
        findViewById(R.id.btnGetAntiCode).setOnClickListener(this);
        findViewById(R.id.btnSaveClassName).setOnClickListener(this);
        findViewById(R.id.btnResetClassName).setOnClickListener(this);
        findViewById(R.id.btnSaveModifyTime).setOnClickListener(this);
        findViewById(R.id.btnSaveMaxSteps).setOnClickListener(this);
        findViewById(R.id.btnSaveDis).setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        SharedPreferences.Editor editor = preferences.edit();
        switch (view.getId()) {
            case R.id.btnGetAntiCode: {
                zjwhVersionName = getZjwhVersionName(getApplicationContext());
                if (isEmpty(zjwhVersionName)) {
                    Toast.makeText(getApplicationContext(), R.string.toast_get_zjwh_version_fail, Toast.LENGTH_SHORT).show();
                } else {
                    getVersionAntiCode();
                }
                return;
            }
            case R.id.btnSaveClassName: {
                String className = edtClassName.getText().toString();
                if (isEmpty(className)) {
                    Toast.makeText(getApplicationContext(), R.string.toast_empty_class, Toast.LENGTH_SHORT).show();
                    return;
                }
                editor.putString(KEY_CLASS_NAME, className);
                Toast.makeText(getApplicationContext(), R.string.toast_save_class, Toast.LENGTH_SHORT).show();
                break;
            }
            case R.id.btnResetClassName: {
                editor.remove(KEY_CLASS_NAME);
                Toast.makeText(getApplicationContext(), R.string.toast_default_class, Toast.LENGTH_SHORT).show();
                break;
            }
            case R.id.btnSaveModifyTime: {
                String startTime = edtStartTime.getText().toString().trim();
                String endTime = edtEndTime.getText().toString().trim();
                if (isEmpty(startTime) || isEmpty(endTime)) {
                    Toast.makeText(getApplicationContext(), R.string.toast_empty_datetime, Toast.LENGTH_SHORT).show();
                    return;
                }
                try {
                    long start = dateFormat.parse(startTime).getTime();
                    long end = dateFormat.parse(endTime).getTime();
                    editor.putLong(KEY_START_TIME, start).putLong(KEY_END_TIME, end);
                    Toast.makeText(getApplicationContext(), R.string.toast_save_datetime, Toast.LENGTH_SHORT).show();
                    break;
                } catch (ParseException ignore) {
                    Toast.makeText(getApplicationContext(), R.string.taost_input_datetime_error, Toast.LENGTH_SHORT).show();
                }
                return;
            }
            case R.id.btnSaveMaxSteps: {
                String maxSteps = edtMaxSteps.getText().toString().trim();
                if (isEmpty(maxSteps)) {
                    Toast.makeText(getApplicationContext(), R.string.toast_empty_steps, Toast.LENGTH_SHORT).show();
                    return;
                }
                try {
                    int steps = Integer.parseInt(maxSteps);
                    editor.putInt(KEY_MAX_STEPS, steps);
                    Toast.makeText(getApplicationContext(), R.string.toast_save_steps, Toast.LENGTH_SHORT).show();
                    break;
                } catch (NumberFormatException ignore) {
                    Toast.makeText(getApplicationContext(), R.string.toast_input_steps_error, Toast.LENGTH_SHORT).show();
                }
                return;
            }
            case R.id.btnSaveDis: {
                String maxDis = edtDisModify.getText().toString().trim();
                if (isEmpty(maxDis)) {
                    Toast.makeText(getApplicationContext(), R.string.toast_empty_dis, Toast.LENGTH_SHORT).show();
                    return;
                }
                try {
                    int dis = Integer.parseInt(maxDis);
                    editor.putInt(KEY_MAX_DIS, dis);
                    Toast.makeText(getApplicationContext(), R.string.toast_save_dis, Toast.LENGTH_SHORT).show();
                    break;
                } catch (NumberFormatException ignore) {
                    Toast.makeText(getApplicationContext(), R.string.toast_input_dis_error, Toast.LENGTH_SHORT).show();
                }
                return;
            }
            default: {
                return;
            }
        }
        editor.apply();
    }

    public static boolean isEmpty(CharSequence sequence) {
        return TextUtils.isEmpty(sequence);
    }

    @Override
    public void onBackPressed() {
        finishAndRemoveTask();
        super.onBackPressed();
    }

    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
        SharedPreferences.Editor editor = preferences.edit();
        switch (compoundButton.getId()) {
            case R.id.shTimeModify: {
                editor.putBoolean(KEY_MODIFY_TIME, b);
                rlTimeModify.setVisibility(b ? View.VISIBLE : View.GONE);
                break;
            }
            case R.id.shStepsModify: {
                editor.putBoolean(KEY_MODIFY_STEPS, b);
                rlStepsModify.setVisibility(b ? View.VISIBLE : View.GONE);
                break;
            }
            case R.id.shUpdateNoReboot: {
                editor.putBoolean(KEY_UPDATE_REBOOT, b);
                break;
            }
            case R.id.shDisModify: {
                editor.putBoolean(KEY_MODIFY_DIS, b);
                rlDisModify.setVisibility(b ? View.VISIBLE : View.GONE);
                break;
            }
            case R.id.shPointPass: {
                editor.putBoolean(KEY_POINT_PASS, b);
                break;
            }
            case R.id.shRemoveAds: {
                editor.putBoolean(KEY_REMOVE_ADS, b);
                break;
            }
            default: {
                return;
            }
        }
        editor.apply();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.setting_memu, menu);
        menu.findItem(R.id.itemCurrentVersion).setTitle(String.format("当前版本：%s", BuildConfig.VERSION_NAME));
        menu.findItem(R.id.itemModuleState).setTitle(getModuleState());
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.itemCheckUpdate) {
            Toast.makeText(getApplicationContext(), R.string.toast_check_update, Toast.LENGTH_SHORT).show();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    final StringBuilder toastStr = new StringBuilder();
                    try {
                        JSONObject rootObject = new JSONObject(Http.readUrlString(Http.UPDATE_URL));
                        if (BuildConfig.VERSION_CODE >= rootObject.optInt(JSON_VERSION_CODE, BuildConfig.VERSION_CODE)) {
                            toastStr.append("没有检测到新版本");
                        } else {
                            toastStr.append("检测到新版本");
                            Message msg = new Message();
                            msg.what = HAVE_UPDATE;
                            msg.obj = rootObject;
                            updateHandler.sendMessage(msg);
                        }
                    } catch (IOException | JSONException e) {
                        toastStr.append("检查更新失败！").append(e.toString());
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), toastStr.toString(), Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            }).start();
        } else if (item.getItemId() == R.id.itemProjectUrl) {
            try {
                startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Http.PROJECT_URL)));
            } catch (RuntimeException ignore) {
                Toast.makeText(getApplicationContext(), R.string.toast_no_browser, Toast.LENGTH_SHORT).show();
            }
        }
        return super.onOptionsItemSelected(item);
    }

    private void showUpdateDialog(final JSONObject rootObject) {
        AlertDialog dialog = new AlertDialog.Builder(SettingActivity.this)
                .setTitle("我爱运动V" + rootObject.optString(JSON_VERSION_NAME, BuildConfig.VERSION_NAME))
                .setMessage("本次更新内容如下：\n\n" + rootObject.optString(JSON_DESCRIPTION, "更新内容已被吃掉......"))
                .setNegativeButton("更新", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        downloadApk(rootObject.optString(JSON_DOWNLOAD_URL, ""));
                    }
                })
                .setPositiveButton("暂不更新", null)
                .create();
        dialog.show();
    }

    /**
     * 原本该方法的目的是解析完下载链接然后自动下载APK并提示用户安装，但由于蓝奏云采用重定向方式进行下载，懒得去解决
     * 所以在解析完下载链接之后，调用浏览器进行下载（需要用户确认操作），虽然这样比较麻烦，但总比进入蓝奏云页面点击下载好
     *
     * @param downloadUrl 从Gitee仓库获取的APK更新链接（链接由蓝奏云提供）
     */
    private void downloadApk(final String downloadUrl) {
        if (!isEmpty(downloadUrl)) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Matcher iframeMatcher = PATTERN_IFRAME_URL.matcher(Http.readUrlString(downloadUrl));
                        if (iframeMatcher.find()) {
                            String iframeUrl = "https://www.lanzous.com" + iframeMatcher.group();
                            iframeMatcher = PATTERN_DOWNLOAD_URL.matcher(Http.readUrlString(iframeUrl));
                            if (iframeMatcher.find()) {
                                Http.ajaxmReferershUrl = iframeUrl;
                                JSONObject rootObject = new JSONObject(
                                        Http.readUrlString(
                                                Http.DOWNLOAD_URL,
                                                String.format(Http.DOWNLOAD_URL_FORMAT, iframeMatcher.group())
                                        ).replaceAll("\\/", "/")
                                );
                                startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(
                                        rootObject.optString(JSON_DOM, "") +
                                                "/file/" + rootObject.optString(JSON_URL, "")
                                )));
                                return;
                            }
                        }
                        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(downloadUrl)));
                    } catch (IOException | JSONException | RuntimeException e) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(getApplicationContext(), e instanceof RuntimeException ? R.string.toast_no_browser : R.string.toast_get_download_url_fail, Toast.LENGTH_SHORT).show();
                            }
                        });
                        return;
                    }
                }
            }).start();
        } else {
            Toast.makeText(getApplicationContext(), R.string.toast_get_download_url_fail, Toast.LENGTH_SHORT).show();
        }
    }

    private void getVersionAntiCode() {
        if (antiCodeRunnable == null) {
            antiCodeRunnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        final JSONObject antiObject = new JSONObject(Http.readUrlString(Http.ANTI_CODE_URL));
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                edtClassName.setText(antiObject.optString(zjwhVersionName, ""));
                                Toast.makeText(getApplicationContext(), isEmpty(edtClassName.getText().toString().trim()) ? R.string.toast_get_zjwh_code_null : R.string.toast_get_zjwh_code_success, Toast.LENGTH_SHORT).show();
                            }
                        });
                    } catch (IOException | JSONException ignore) {
                    }
                }
            };
        }
        try {
            Thread thread = new Thread(antiCodeRunnable);
            thread.start();
            thread.join();
        } catch (InterruptedException ignore) {
        }
    }

    private String getModuleState() {
        return "模块未激活";
    }

    public static String getZjwhVersionName(Context baseContext) {
        try {
            return baseContext.getPackageManager().getPackageInfo(PKG, 0).versionName;
        } catch (PackageManager.NameNotFoundException ignore) {
            return "";
        }
    }
}
